package com.hmall.search.service.impl;

import cn.hutool.core.util.ObjectUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionBoostMode;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import com.hmall.common.domain.PageDTO;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.domain.query.ItemPageQuery;
import com.hmall.search.service.ISearchService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
@Slf4j
@AllArgsConstructor
public class SearchServiceImpl implements ISearchService {

    private final ElasticsearchClient esClient;


    @Override
    public PageDTO<ItemDoc> search(ItemPageQuery query) throws IOException {
        try {
            SearchResponse<ItemDoc> response = esClient.search(builder -> builder.index("items")
                    .query(qb->qb.functionScore(fsBuilder->fsBuilder.query(queryBuilder->queryBuilder.bool(boolBuilder-> {
                        if (ObjectUtil.isNotEmpty(query.getKey())){
                            boolBuilder.must(mustBuilder->mustBuilder.match(matchBuilder->matchBuilder.field("name").query(query.getKey())));
                        }
                        if(ObjectUtil.isNotEmpty(query.getCategory())){
                            boolBuilder.filter(filterBuilder->filterBuilder.term(termBuilder->termBuilder.field("category").value(query.getCategory())));
                        }
                        if(ObjectUtil.isNotEmpty(query.getBrand())){
                            boolBuilder.filter(filterBuilder->filterBuilder.term(termBuilder->termBuilder.field("brand").value(query.getBrand())));
                        }
                        if(ObjectUtil.isNotNull(query.getMaxPrice())){
                            boolBuilder.filter(filterBuilder->filterBuilder.range(rgBuilder->rgBuilder.field("price").lte(JsonData.of(query.getMaxPrice()))));
                        }
                        if (ObjectUtil.isNotNull(query.getMinPrice())){
                            boolBuilder.filter(filterBuilder->filterBuilder.range(termBuilder->termBuilder.field("price").gte(JsonData.of(query.getMinPrice()))));
                        }
                        return  boolBuilder;
                    }
                    )).functions(ftBuilder->ftBuilder.
                            filter(fb->fb.term(tb->tb.field("isAD").value(true))).weight(100D)).boostMode(FunctionBoostMode.Multiply)))
                    .sort(sortOpsBuilder->sortOpsBuilder.field(fdB->{
                        if(ObjectUtil.isEmpty(query.getSortBy())){
                            fdB.field("_score").order(SortOrder.Desc);
                        }else {
                            fdB.field(query.getSortBy()).order(query.getIsAsc()?SortOrder.Asc:SortOrder.Desc);
                        }return fdB;
                    }))
                    .from(query.getPageSize()*(query.getPageNo()-1)).size(query.getPageSize())
                    .highlight(hb->hb.fields("name",hlb->hlb.preTags("<em style='color:red;'>").postTags("</em>"))), ItemDoc.class);
            PageDTO<ItemDoc> dto =new PageDTO<>();
            long total = response.hits().total().value();
            dto.setTotal(total);
            List<Hit<ItemDoc>> hits = response.hits().hits();
           List<ItemDoc> docList =hits.stream().map(hit->{
               ItemDoc doc = hit.source();
               Map<String,List<String>> map =hit.highlight();
               if(ObjectUtil.isNotEmpty(map)){
                   List<String> nameValues = map.get("name");
                   if(ObjectUtil.isNotEmpty(nameValues)){
                       doc.setName(nameValues.get(0));
                   }
               }
               return doc;
           }).collect(Collectors.toList());
                dto.setList(docList);
            return dto;
        } catch (Exception e) {
            log.error("搜索商品时发生错误", e);
            throw e;
        }
    }
}
